﻿using System;
using System.IO;
using System.Text;
using System.Threading;					
using System.Diagnostics; 				
using System.Net; 						
using System.Collections.Specialized;
using System.Timers;
using System.Linq;

using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Profiles;
using Styx.Pathing;						
using Styx.WoWInternals;				
using Styx.WoWInternals.WoWObjects;		
using Styx.Plugins;						

namespace rndbuddy
{
    public class rndbuddy : HBPlugin
    {
        public override string Name { get { return "rndbuddy"; } }
        public override string Author { get { return "roboto"; } }
        public override Version Version { get { return settings.version; } }
        public override bool WantButton { get { return true; } }
        public override void OnButtonPress() { var fr = new gui_settings(); fr.Show(); }
        public override string ButtonText { get { return "Settings"; } }
        public override void Pulse() { _Pulse(); }

        public static Toon _toon = new Toon();

        bool start_ok = false;
        public static bool _init = false;
        bool firstProfile = false;

        //Command Pipe
        public static System.Timers.Timer cmdpipe;
        //crowd sourcer
        public static System.Timers.Timer crowds;
        //targeting Timer
        public static System.Timers.Timer watch_target;
        //heartbeat timer
        public static System.Timers.Timer t_heartbeat;
        //movement timer
        public static System.Timers.Timer t_movement;
        //screenshot timer
        public static System.Timers.Timer t_screenshot;
        //reload controller (on end)
        public static System.Timers.Timer t_dynreload;
        //timed reloader
        public static System.Timers.Timer t_profile;
        //toon data refresh
        public static System.Timers.Timer t_toon;        
        //timer to make sure i was started
        static System.Timers.Timer _t_startok;

        //Start
        public override void Initialize()
        {
            setStatusText("Starting rndbuddy");
            log("Starting rndbuddy Version "+settings.version.ToString());

            //populate toon container
            _toon.poll();
            
            //initial data for movement_watcher
            movement_watcher.last_moved = System.DateTime.Now;
            movement_watcher.tried_bot_restart = false;
            movement_watcher.tried_move_to_unstuck = false;

            if (!settings.am_i_up_to_date())
            {
                string error = "Your plugin is not up to date! Please run SVN Update!";
                rndbuddy.log(error);
                TreeRoot.Stop(error);
                return;
            }

            //welcome user
            rndbuddy.log(server_pipe.welcome_message());

            //parse settings from file
            start_ok = settings.begin();
            if (!start_ok)
            {
                log("Your API Key failed validation. Please get one at settings tab!");
                return;   
            }

            //check for needed files in AppDataDir & Update
            settings.download_files_to_app_data();
            
            //timers
            rndbuddy.log("Bringing up Timers");
            rndbuddy.logsilent("Setting Bringing up Command Timer...");
            cmdpipe = new System.Timers.Timer(15000);
            cmdpipe.Elapsed += new ElapsedEventHandler(cmdpipe_ticker);
            cmdpipe.Enabled = true;
            cmdpipe.Start();

            rndbuddy.logsilent("Setting Bringing up Crowdsourcer...");
            crowds = new System.Timers.Timer(4000);
            crowds.Elapsed += new ElapsedEventHandler(crowds_ticker);
            crowds.Enabled = true;
            crowds.Start();

            rndbuddy.logsilent("Setting Bringing up Targeting Watcher...");
            watch_target = new System.Timers.Timer(2500);
            watch_target.Elapsed += new ElapsedEventHandler(watch_target_ticker);
            watch_target.Enabled = true;
            watch_target.Start();

            rndbuddy.logsilent("Setting Bringing up Heartbeat");
            t_heartbeat = new System.Timers.Timer(10000);
            t_heartbeat.Elapsed += new ElapsedEventHandler(heartbeat);
            t_heartbeat.Enabled = true;
            t_heartbeat.Start();

            rndbuddy.logsilent("Setting Bringing up Movement Watcher");
            t_movement = new System.Timers.Timer(5000);
            t_movement.Elapsed += new ElapsedEventHandler(movement_check);
            t_movement.Enabled = true;
            t_movement.Start();

            rndbuddy.logsilent("Setting Bringing up Screenshot Timer");
            rndbuddy.logsilent("Setting Setting Interval to " + settings.screenshot_interval_min + " minutes");
            t_screenshot = new System.Timers.Timer(settings.screenshot_interval_min * 60 * 1000);
            t_screenshot.Elapsed += new ElapsedEventHandler(tick_screenshot);
            t_screenshot.Enabled = true;
            t_screenshot.Start();

            rndbuddy.logsilent("Setting Bringing up dynamic reload detector");
            t_dynreload = new System.Timers.Timer(settings.dyn_reload_check * 1000);
            t_dynreload.Elapsed += new ElapsedEventHandler(ProfileHandler.dyn_reload_check);
            t_dynreload.Enabled = true;
            t_dynreload.Start();

            rndbuddy.logsilent("Setting Bringing up Profile Reloader");
            t_profile = new System.Timers.Timer(settings.refresh_time * 60 * 1000);
            t_profile.Elapsed += new ElapsedEventHandler(ProfileHandler.timed_reload);
            t_profile.Enabled = true;
            t_profile.Start();

            rndbuddy.logsilent("Setting Bringing up Toon");
            t_toon = new System.Timers.Timer(10000);
            t_toon.Elapsed += new ElapsedEventHandler(rndbuddy.update_toon);
            t_toon.Enabled = true;
            t_toon.Start();

            rndbuddy.logsilent("Setting Bringing up Secure-Start Timer");
            _t_startok = new System.Timers.Timer(25000);
            _t_startok.Elapsed += new ElapsedEventHandler(rndbuddy._provide_secured_start);
            _t_startok.Enabled = true;
            _t_startok.Start();

            //add event handlers
            rndbuddy.log("Adding Game and Bot Event Handlers");
            Logging.OnLogMessage += LogWatcher.Logging_OnLogMessage;
            Lua.Events.AttachEvent("GUILD_INVITE_REQUEST", GuildInviteAccepter.GuildInvite);
            Chat.Whisper += PlayerSecurity.Chat_Whisper;
            //Chat.Channel += PlayerSecurity.Chat_Channel;
            Chat.Guild += PlayerSecurity.Chat_Guild;
            Chat.Party += PlayerSecurity.Chat_Party;
            Lua.Events.AttachEvent("CHAT_MSG_BN_WHISPER", PlayerSecurity.BNetWhisper);
            Styx.CommonBot.BotEvents.Player.OnPlayerDied += trigger_death;
            Styx.CommonBot.BotEvents.Player.OnLevelUp += trigger_lvlup;

            //essential init ok, following steps are not mandatory
            _init = true;

            //set botbase to GB2 if not already or Professionbuddy only if trackonly feature is disabled
            if (!BotController.is_gather_botbase() && !settings.enable_opt_trackonly)
            {
                rndbuddy.log("Settings Botbase to Gatherbuddy");
                var Bot = BotManager.Instance.Bots.FirstOrDefault(kvp => kvp.Key == "Gatherbuddy2");
                if (Bot.Key == "Gatherbuddy2")
                {
                    BotManager.Instance.SetCurrent(Bot.Value);
                    //overwrite GB2 settings
                    if (settings.overwrite_gb2_settings)
                        settings.default_gb2_settings();
                }
                else
                {
                    rndbuddy.log("Unable to locate Gatherbuddy2 Botbase");
                    _init = false;
                    return;
                }
            }

            //informal settings check
            settings.check_character_settings();

            //load a new profile on startup
            if (!firstProfile && BotController.is_gather_botbase())
            {
                log("Getting initial first Profile");
                ProfileHandler.get_profile();
                firstProfile = true;
            }
            else
            {
                //dummy settings
                ProfileHandler.current_profile = "none";
                ProfileHandler.profile_zone = "none";
                ProfileHandler.last_node[0] = "0";
                ProfileHandler.last_node[1] = "0";
            }

            //autostart?
            if (settings.autostart && BotController.is_gather_botbase())
                TreeRoot.Start();

            //notification of beta build
            if (server_pipe.server.Contains("test"))
                rndbuddy.logcrit("Your are using a rndbuddy TEST Release! There *will* be bugs");

            if (BotController.is_mixed_gb2())
                rndbuddy.logcrit("WARNING! Bans with DungeonBuddy and BGBuddy are very plausible! Do not blame rndbuddy if you get banned!!1111");
        }

        public static void update_toon(object sender, ElapsedEventArgs e)
        {
            if (!StyxWoW.IsInGame || !StyxWoW.IsInWorld)
                return;

            Toon _new_toon = new Toon();
            _new_toon.poll();

            if (_toon.Name != _new_toon.Name)
            {
                rndbuddy.log("Toon Name hash has changed!");
                rndbuddy.log("Old Toon Data:");
                rndbuddy.log("Name = " + _toon.Name);
                rndbuddy.log("Realm = " + _toon.Realm);
                rndbuddy.log("New Toon Data:");
                rndbuddy.log("Name = " + _new_toon.Name);
                rndbuddy.log("Realm = " + _new_toon.Realm);
                rndbuddy.logcrit("Not updating toon data!");
                return;
            }
            _toon = _new_toon;
            return;
        }

        
        public override void Dispose()
        {
            rndbuddy.log("Disposing rndbuddy");
            Logging.OnLogMessage -= LogWatcher.Logging_OnLogMessage;
            Lua.Events.DetachEvent("GUILD_INVITE_REQUEST", GuildInviteAccepter.GuildInvite);
            Chat.Whisper -= PlayerSecurity.Chat_Whisper;
            Chat.Channel -= PlayerSecurity.Chat_Channel;
            Chat.Guild -= PlayerSecurity.Chat_Guild;
            Chat.Party -= PlayerSecurity.Chat_Party;
            Lua.Events.DetachEvent("CHAT_MSG_BN_WHISPER", PlayerSecurity.BNetWhisper);
            t_dynreload.Dispose();
            t_heartbeat.Dispose();
            t_movement.Dispose();
            t_screenshot.Dispose();
                
        }

        private void _Pulse()
        {
            if (!_init)
            {
                rndbuddy.logcrit("rndbuddy was not able to start up. Please check log!");
                TreeRoot.Stop("rndbuddy was not able to start up. Please check log!");
                return;
            }

            if (!StyxWoW.IsInGame || !StyxWoW.IsInWorld)
                return;

            if (Battlegrounds.IsInsideBattleground || StyxWoW.Me.IsInInstance || StyxWoW.Me.IsOnTransport)
                return;
            
            if (StyxWoW.Me.Combat)
                return;

            //timed kill
            if (DateTime.Now > settings.timed_kill)
            {
                rndbuddy.log("Killing bot. Timed kill reached.");
                rndbuddy.forceQuit(true, "Maximum Time reached");
            }

            //pause when on PB but not in desired zone (=> auctioning)
            if (BotController.is_professionbuddy() && StyxWoW.Me.MapId != Convert.ToInt32(ProfileHandler.pb_desired_zone))
            {
                if (rndbuddy.t_profile.Enabled)
                {
                    rndbuddy.log("Bot is not in desired zone, pausing Reload Timer");
                    rndbuddy.t_profile.Stop();
                }
                return;
            }

            //dont pulse when not gatherbuddy or professionbuddy
            if (!BotController.is_gather_botbase())
            {
                if (t_profile.Enabled)
                {
                    rndbuddy.log("Not on a gathering botbase. Stopping profile reloader");
                    t_profile.Stop();
                }
                return;
            }

            //movement controller dismounter
            if (settings.enable_unmount_module == 1)
                movement_controller.check_mounting();

            if (!t_profile.Enabled)
            {
                rndbuddy.log("Restarting profile reloader");
                t_profile.Start();
            }
            
            return;
        }

        /* RNDBUDDY LOGGING FACTORIES */
        public static void log(string message)
        {
            Logging.Write(System.Windows.Media.Color.FromRgb(80,180,240),"[rndbuddy] " + message);
        }

        public static void logcrit(string message)
        {
            Logging.Write(System.Windows.Media.Color.FromRgb(240, 10, 178), "[rndbuddy critical] " + message);
        }

        public static void logsilent(string message)
        {
            Logging.WriteDiagnostic("[rndbuddy silent] "+message);
        }

        /*TIMER EVENTS */
        public static void heartbeat(object sender, ElapsedEventArgs e)
        {
            server_pipe.heartbeat();
        }

        public static void movement_check(object sender, ElapsedEventArgs e)
        {
            movement_watcher.trk_move();
        }

        public static void watch_target_ticker(object sender, ElapsedEventArgs e)
        {
            PlayerSecurity.who_is_targeting_me(false);
        }

        public static void cmdpipe_ticker(object sender, ElapsedEventArgs e)
        {
            string cmd = server_pipe.get_pending_command();
            if (cmd != "null")
                CMDPipe.issue_command(cmd);
        }

        public static void crowds_ticker(object sender, ElapsedEventArgs e)
        {
            crowdsourcer.get_nodes();
        }

        public static void forceQuit(bool killReloggers, string reason = "")
        {
            rndbuddy.log("Killing WoW");
            server_pipe.notify_kill(reason);

            Lua.DoString(@"ForceQuit()");
            if (killReloggers)
            {
                //kill reloggers if there are any
                log("Killing all reloggers...");
                Process.GetProcesses().Where(
                    p => 
                        p.ProcessName == "ARelog" || 
                        p.ProcessName == "HBRelog"
                    )
                        .ToList()
                        .ForEach(p => p.Kill());
            }
            Environment.Exit(0);
        }

        public static void setStatusText(string text)
        {
            TreeRoot.StatusText = text;
        }


        public static void trigger_lvlup(BotEvents.Player.LevelUpEventArgs args)
        {
            if (settings.screen_on_events)
                take_screenshot();
            if (settings.notify_on_events)
                server_pipe.notify("Level up! (" + args.NewLevel.ToString() + ")");
        }

        public static void trigger_death()
        {
            if (settings.screen_on_events)
                take_screenshot();
            if (settings.notify_on_events)
                server_pipe.notify("Bot died in " + StyxWoW.Me.ZoneText);
        }

        public static void tick_screenshot(object sender, ElapsedEventArgs e)
        {
            if (settings.do_screenshots)
                take_screenshot();
        }

        public static void take_screenshot()
        {
            rndbuddy.log("Trying to take screenshot");
            try
            {
                Lua.Events.AttachEvent("SCREENSHOT_SUCCEEDED", process_screenshot);
                Lua.DoString("TakeScreenshot()");
            }
            catch (Exception ex)
            {
                rndbuddy.logcrit("Exception in take_screenshot");
                rndbuddy.logcrit(ex.Message);
            }

            //update interval of screenshot timer
            t_screenshot.Interval = (settings.screenshot_interval_min * 60 * 1000);
        }

        public static void process_screenshot(object sender, LuaEventArgs args)
        {
            rndbuddy.log("Processing Screenshot");
            try
            {
                Lua.Events.DetachEvent("SCREENSHOT_SUCCEEDED", process_screenshot);
                server_pipe.uploadLatestScreenshot();
            }
            catch (Exception e)
            {
                rndbuddy.logcrit("Exception while processing screenshot");
                rndbuddy.logcrit(e.Message);
            }
            rndbuddy.log("upload process complete");
        }

        //timer tick makes sure treeroot was started
        private static void _provide_secured_start(object sender, ElapsedEventArgs e)
        {
            if (!TreeRoot.IsRunning && BotController.is_gather_botbase() && BotController.are_there_reloggers())
            {
                rndbuddy.log("Honorbuddy did not start bot. This might be caused by a relogger!");
                if (ProfileManager.CurrentProfile == null || (BotController.is_gatherbuddy() && !ProfileManager.CurrentProfile.Name.Contains("rnd")))
                    ProfileHandler.reload_profile();
                TreeRoot.Start();
            }
            _t_startok.Enabled = false;
            _t_startok.Dispose();
            rndbuddy.log("SafeStart Timer disposed!");
        }

    }
}
